#include "Profiler.h"
#include <cassert>

int ProfileSample::lastOpenedSample = -1;
int ProfileSample::openSampleCount = 0;
ProfileSample::profileSample ProfileSample::samples[MAX_PROFILER_SAMPLES];
ProfilerOutputHandler *ProfileSample::outputHandler = 0;
float ProfileSample::rootBegin = 0.0f;
float ProfileSample::rootEnd = 0.0f;
bool ProfileSample::bProfilerIsRunning = true;

ProfileSample::ProfileSample(string sampleName)
{
	if (!bProfilerIsRunning)
		return;

	int i, storeIndex = -1;

	for (i = 0; i < MAX_PROFILER_SAMPLES; ++i) {
		if (!samples[i].bIsValid) {
			if (storeIndex < 0)
				storeIndex = i;
		}else {
			if (samples[i].name == sampleName) {
				assert(!samples[i].bIsOpen && "Tried to profile a sample which was already being profiled");
				iSampleIndex = i;
				iParentIndex = lastOpenedSample;
				lastOpenedSample = i;
				samples[i].parentCount = openSampleCount;
				++openSampleCount;
				samples[i].bIsOpen = true;
				++samples[i].callCount;
				samples[i].startTime = GetTime();
				if (iParentIndex < 0)
					rootBegin = samples[i].startTime;
				return;
			}
		}
	}

	//we've not found it, so it must be a new sample
	//use the storeIndex value to store the new sample
	assert(storeIndex >= 0 && "Profiler has run out of sample slots!");
	samples[storeIndex].bIsValid = true;
	samples[storeIndex].name = sampleName;
	iSampleIndex = storeIndex;
	iParentIndex = lastOpenedSample;
	lastOpenedSample = storeIndex;
	samples[i].parentCount = openSampleCount;
	openSampleCount++;
	samples[storeIndex].bIsOpen = true;
	samples[storeIndex].callCount = 1;

	samples[storeIndex].totalTime = 0.0f;
	samples[storeIndex].childTime = 0.0f;
	samples[storeIndex].startTime = GetTime();
	if (iParentIndex < 0)
		rootBegin = samples[storeIndex].startTime;
}

ProfileSample::~ProfileSample()
{
	if (!bProfilerIsRunning)
		return;

	float fEndTime = GetTime();
	samples[iSampleIndex].bIsOpen = false;
	float fTimeTaken = fEndTime - samples[iSampleIndex].startTime;

	if (iParentIndex >= 0)
		samples[iParentIndex].childTime += fTimeTaken;
	else
		rootEnd = fEndTime;

	samples[iSampleIndex].totalTime += fTimeTaken;
	lastOpenedSample = iParentIndex;
	--openSampleCount;
}

void ProfileSample::Output()
{
	if (!bProfilerIsRunning)
		return;

	assert(outputHandler && "Profiler has no output handler set");
	outputHandler->BeginOutput(rootEnd - rootBegin);

	for(int i = 0; i < MAX_PROFILER_SAMPLES; ++i)
	{
		if (samples[i].bIsValid) {
			float sampleTime, percentage;
			
			sampleTime = samples[i].totalTime-samples[i].childTime;
			percentage = ( sampleTime / ( rootEnd - rootBegin ) ) * 100.0f;

			float totalPc;
			totalPc = samples[i].averagePc*samples[i].dataCount;
			totalPc += percentage; samples[i].dataCount++;
			samples[i].averagePc = totalPc/samples[i].dataCount;
			if ((samples[i].minPc == -1) || (percentage < samples[i].minPc)) samples[i].minPc = percentage;
			if ((samples[i].maxPc == -1) || (percentage > samples[i].maxPc)) samples[i].maxPc = percentage;

			outputHandler->Sample(samples[i].minPc,
					      samples[i].averagePc,
					      samples[i].maxPc,
					      sampleTime,
					      samples[i].callCount,
					      samples[i].name,
					      samples[i].parentCount);

			//reset the sample for next time
			samples[i].callCount = 0;
			samples[i].totalTime = 0;
			samples[i].childTime = 0;
		}
	}

	outputHandler->EndOutput();
}

void ProfileSample::ResetSample(string strName)
{
	for(int i = 0; i < MAX_PROFILER_SAMPLES; ++i) {
		if ((samples[i].bIsValid) && (samples[i].name == strName)) {
			//found it
			//reset avg/min/max ONLY
			//because the sample may be running
			samples[i].maxPc = samples[i].minPc = -1;
			samples[i].dataCount = 0;
			return;
		}
	}
}

void ProfileSample::ResetAll()
{
	for(int i = 0; i < MAX_PROFILER_SAMPLES; ++i) {
		if (samples[i].bIsValid) {
			samples[i].maxPc=samples[i].minPc = -1;
			samples[i].dataCount = 0;
		}
	}
}